import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.StringTokenizer;

public class Parser {

	ArrayList<Type> types = new ArrayList<Type>();
	State init;
	State goal;
	ArrayList<String> variables = new ArrayList<String>();
	ArrayList<Action> actions = new ArrayList<Action>();

	static Hashtable<Integer, String> stringi = new Hashtable<Integer, String>();
	
	public void parse(File f) throws ParserException {
		FileReader fr = null;
		try {
			fr = new FileReader(f);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		char[] buff = new char[(int) f.length()];
		try {
			if (fr != null) {
				fr.read(buff);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		StringTokenizer st = new StringTokenizer(new String(buff));
		String s = st.nextToken();
		if (!s.equals("Initial_state:")) {
			throw new ParserException();
		}
		init = parseState(st);
		variables=init.getValues();
		s = st.nextToken();
		if (!s.equals("Goal_state:")) {
			throw new ParserException();
		}
		goal = parseState(st);
		s = st.nextToken();
		if (!s.equals("Actions:")) {
			throw new ParserException();
		}
		while (st.hasMoreTokens()) {
			actions.add(parseAction(st));
		}
		ArrayList<Action> actions2 = new ArrayList<Action>();
		for (Action a : actions) {
			switch (a.arguments.size()) {
			case 1:
				actions2.addAll(generate1(a));
				break;
			case 2:
				actions2.addAll(generate2(a));
				break;
			case 3:
				actions2.addAll(generate3(a));
				break;
			default:
				throw new ParserException();
			}
		}
		actions = actions2;
		for (Action a : actions) {
			a.populateVariables();
			stringi.put(a.hashCode(), a.toString());
		}
		System.out.println(actions.size());
	}

	private ArrayList<Action> generate3(Action a) {
		ArrayList<Action> wyn = new ArrayList<Action>();
		for (String v1 : variables) {
			for (String v2 : variables) {
				for (String v3 : variables) {
					if (v1!=v2 && v1!=v3 && v2!=v3) {
						Action b = a.copy();
						b.arguments.get(0).value = v1;
						b.arguments.get(1).value = v2;
						b.arguments.get(2).value = v3;
						wyn.add(b);
					}
				}
			}
		}
		return wyn;
	}

	private ArrayList<Action> generate2(Action a) {
		ArrayList<Action> wyn = new ArrayList<Action>();
		for (String v1 : variables) {
			for (String v2 : variables) {
				if (v1!=v2) {
					Action b = a.copy();
					b.arguments.get(0).value = v1;
					b.arguments.get(1).value = v2;
					wyn.add(b);
				}
			}
		}
		return wyn;
	}

	private ArrayList<Action> generate1(Action a) {
		ArrayList<Action> wyn = new ArrayList<Action>();
		for (String v : variables) {
			Action b = a.copy();
			b.arguments.get(0).value = v;
			wyn.add(b);
		}
		return wyn;
	}

	/*private Variable parseVariable(String s) {
		Variable v = new Variable();
		StringBuilder nazwa = new StringBuilder();
		boolean done = false;
		for (int i = 0; !done; i++) {
			if (s.charAt(i) == '(') {
				done = true;
				s = s.substring(i + 1);
				String value = nazwa.toString();
				int key = value.hashCode();
				v.name = key;
				stringi.put(key, value);
				nazwa = new StringBuilder();
				boolean done2 = false;
				for (int j = 0; !done2; j++) {
					if (s.charAt(j)==','){
						value = nazwa.toString();
						key = value.hashCode();
						stringi.put(key, value);
						//v.types.add(getType(key));
						nazwa = new StringBuilder();
					} else if (s.charAt(j) == ')') {
						done2 = true;
						value = nazwa.toString();
						key = value.hashCode();
						stringi.put(key, value);
						//v.types.add(getType(key));
					} else {
						nazwa.append(s.charAt(j));
					}
				}
			} else {
				nazwa.append(s.charAt(i));
			}
		}
		return v;
	}

	private Type getType(int string) {
		Type wyn = null;
		for (Type t : types) {
			if (t.name==string) {
				wyn = t;
			}
		}
		return wyn;
	}*/

	private Action parseAction(StringTokenizer st) throws ParserException {
		Action a = new Action();
		String s = st.nextToken("\r\n\t");
		StringTokenizer st2 = new StringTokenizer(s, " _(),");
		String value = st2.nextToken();
		int key = value.hashCode();
		stringi.put(key, value);
		a.name = key;
		while (st2.hasMoreTokens()) {
			ActionArgument aa = new ActionArgument();
			value = st2.nextToken();
			key = value.hashCode();
			stringi.put(key, value);
			aa.nazwa = value;
			a.arguments.add(aa);
		}
		s = st.nextToken(" \r\n\t\f");
		if (!s.equals("Preconditions:")) {
			throw new ParserException();
		}
		a.pre = parseState(st);
		s = st.nextToken();
		if (!s.equals("Postconditions:")) {
			throw new ParserException();
		}
		a.post = parseState(st);
		return a;
	}

	private State parseState(StringTokenizer st) {
		String s = st.nextToken();
		Hashtable<Integer, Variable> variables = new Hashtable<Integer, Variable>();
		boolean not = false;
		if (s.equals("not")) {
			not = true;
			s = st.nextToken();
		}
		while (s.charAt(s.length() - 1) == ',') {
			parseSt(s, variables, not);
			s = st.nextToken();
			not = false;
			if (s.equals("not")) {
				not = true;
				s = st.nextToken();
			}
		}
		parseSt(s, variables, not);
		State sta = new State();
		sta.variables = variables;
		return sta;
	}

	private void parseSt(String s, Hashtable<Integer, Variable> variables,
			boolean not) {
		StringBuilder nazwa = new StringBuilder();
		Variable v = new Variable();
		if (not) {
			v.positive = false;
		}
		boolean done = false;
		for (int i = 0; !done; i++) {
			if (s.charAt(i) == '(') {
				done = true;
				s = s.substring(i + 1);
				String value = nazwa.toString();
				int key = value.hashCode();
				stringi.put(key, value);
				v.name = key;
				//v.types = getTypesFromVar(v.name);
				nazwa = new StringBuilder();
				boolean done2 = false;
				for (int j = 0; !done2; j++) {
					if (s.charAt(j) == ',') {
						value = nazwa.toString();
						key = value.hashCode();
						stringi.put(key, value);
						v.values.add(value);
						nazwa = new StringBuilder();
					} else if (s.charAt(j) == ')') {
						done2 = true;
						value = nazwa.toString();
						key = value.hashCode();
						stringi.put(key, value);
						v.values.add(value);
						variables.put(v.hashCode(), v);
					} else {
						nazwa.append(s.charAt(j));
					}
				}
			} else {
				nazwa.append(s.charAt(i));
			}
		}
	}

	/*private ArrayList<Type> getTypesFromVar(int name) {
		ArrayList<Type> wyn = null;
		for (Variable v : variables) {
			if (v.name==name) {
				//wyn = v.types;
			}
		}
		return wyn;
	}

	private Type parseType(StringTokenizer st) {
		Type wyn = null;
		String s = st.nextToken();
		if (!s.equals("Variables:")) {
			wyn = new Type();
			String value = s.substring(0, s.length() - 1);
			int key = value.hashCode();
			stringi.put(key, value);
			wyn.name = key;
			st.nextToken();
			s = st.nextToken();
			while (s.charAt(s.length() - 1) == ',') {
				value = s.substring(0, s.length() - 1);
				key = value.hashCode();
				stringi.put(key, value);
				wyn.values.add(key);
				s = st.nextToken();
			}
			value = s.substring(0, s.length());
			key = value.hashCode();
			stringi.put(key, value);
			wyn.values.add(key);
			st.nextToken();
		}
		return wyn;
	}*/

}
